package pdsgameserver;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.UnsupportedEncodingException;

import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
import com.sun.sgs.app.ManagedObject;
import com.sun.sgs.app.DataManager;
import com.sun.sgs.app.ManagedReference;
import com.sun.sgs.app.NameNotBoundException;


public class Player implements ManagedObject, Serializable, ClientSessionListener
{
	
    public static final String MESSAGE_CHARSET = "UTF-8";  /** The message encoding. */
    private static final long serialVersionUID = 2L;  /** The version of the serialized form of this class. */
    private static final Logger logger = Logger.getLogger(Player.class.getName());  /** The {@link Logger} for this class. */
    private ClientSession session; /** The session this {@code ClientSessionListener} is listening to. */
	private String name;
	private ManagedReference game;       /** Reference to the Game The Player is in*/
	
	/**
	*Create the player
	*	@param session The session to communicate with client
	*/ 
	public Player(ClientSession session)
	{
		DataManager dataManager = AppContext.getDataManager();
        dataManager.markForUpdate(this);
		this.session = session;	
		name = session.getName();
	}
	
	public String getName()
	{
		return name;	
	}
	
	public ClientSession getSession()
	{
		return this.session;	
	}
	
	//Server recieves message from client, process message as command
	public void receivedMessage(byte[] message) 
	{
		logger.log(Level.INFO, "Direct message from {0}", session.getName());
		String details = null;
		String command = null;
		String smessage = decodeString(message); //turn bytes into String
		
		//all commands start with a @ and end with a : 
		int begin = smessage.indexOf('@');
		int end = smessage.indexOf(':');
		
		//if @ and : are not in message than it is not a command
		if(begin == -1 || end == -1)
		{
			session.send(encodeString("Not a valid command"));
			return;
		}
		
		//get the command out of the message
		command = smessage.substring(begin, end);
		
		//if command has details
		if(smessage.length() > end+1)
			details = smessage.substring(end+1);
		
		//process the command
		if(command.equals("@JOIN"))
		{
			DataManager dataManager = AppContext.getDataManager();
			dataManager.markForUpdate(this);
			int index = details.indexOf(":");
			String chanName = details.substring(0, index);
			String gameType = details.substring(index+1);
			try
			{
				Game g = dataManager.getBinding(chanName, Game.class);
				g.join(this);
				setGame(g);
			}
			catch(NameNotBoundException e)
			{
				Game newgame = Game.newGame(chanName, gameType);
				if(newgame == null)
				{
					session.send(encodeString("not valid game type"));
					return;
				} 
				newgame.join(this);
				setGame(newgame);		
			}
		}
		else if(command.equals("@MOVE")) 
		{
			Game g = getGame();
			if(g == null || details == null)
			{
				session.send(encodeString("Not in a Game now"));
				return;
			}
			g.move(this, details);
		}
		else if(command.equals("@LEAVE"))
		{
			Game g = getGame();
			if(g == null)
			{
				session.send(encodeString("Not in a Game now"));
				return;
			}
			g.leave(this);
			this.setGame(null);
		}
		else if(command.equals("@DISC"))
		{	
			session.disconnect();			
		}
		else
		{
			this.send("Not Valid Account");
		}
	}
	
	public void send(String m)
	{
		session.send(encodeString(m));	
	}
	
	public Game getGame()
	{
		if (game == null)
            return null;
        return game.get(Game.class);	
	}
	
	public void setGame(Game g)
	{
		DataManager dataManager = AppContext.getDataManager();
		dataManager.markForUpdate(this);
		if(g == null)
		{
			game = null;
			return;
		}
		game = dataManager.createReference(g);
	}
	
	public void disconnected(boolean graceful) 
	{
        String grace = graceful ? "graceful" : "forced";
        logger.log(Level.INFO, "User {0} has logged out {1}", new Object[] { session.getName(), grace } );
		Game g = getGame();
		if(g != null)
		{
			g.leave(this);
			setGame(null);
		}
		session = null;
		name = null;
		AppContext.getDataManager().removeObject(this);
    }
	
	/**
     * Encodes a {@code String} into an array of bytes.
     *
     * @param s the string to encode
     * @return the byte array which encodes the given string
     */
    public static byte[] encodeString(String s) 
	{
        try 
		{
            return s.getBytes(MESSAGE_CHARSET);
        } 
		catch (UnsupportedEncodingException e) 
		{
            throw new Error("Required character set " + MESSAGE_CHARSET + " not found", e);
        }
    }

    /**
     * Decodes an array of bytes into a {@code String}.
     *
     * @param bytes the bytes to decode
     * @return the decoded string
     */
    public static String decodeString(byte[] bytes) 
	{
        try 
		{
            return new String(bytes, MESSAGE_CHARSET);
        } 
		catch (UnsupportedEncodingException e) 
		{
            throw new Error("Required character set " + MESSAGE_CHARSET + " not found", e);
        }
    }
	
	
}
